﻿/*******************************************************************************
 * AAEngine
 * Copyright (c) 2010 Mike Jarosch
 *
 * Original source PushButton Engine:
 * Copyright (C) 2009 PushButton Labs, LLC
 * For more information see http://www.pushbuttonengine.com
 *
 * This file is licensed under the terms of the MIT license, which is included
 * in the "MIT License.txt" file at the root directory of this SDK.
 ******************************************************************************/

using System.Collections.Generic;
using Microsoft.Xna.Framework;
using AAEngine.Engine;
using AAEngine.Engine.Entities;

namespace AAEngine.Components.Basic
{
    /// <summary>
    /// General purpose component for tracking health.
    /// </summary>
    public class HealthComponent : EntityComponent
    {
        private float _health;
        private float _timeOfLastDamage;
        private IEntity _lastDamageOriginator;

        public float LastDamageFadeTime { get; set; }

        public float MaxHealth { get; set; }

        public bool DestroyOnDeath { get; set; }

        public float DamageMagnitude { get; set; }

        public Dictionary<string, float> DamageModifier { get; set; }

        public HealthComponent()
        {
            LastDamageFadeTime = 200;
            MaxHealth = 100;
            DestroyOnDeath = true;
            DamageMagnitude = 1;
            DamageModifier = new Dictionary<string, float>();

            _health = 100;
            _timeOfLastDamage = 0;
            _lastDamageOriginator = null;
        }

        public override void  OnAdd()
        {
 	         _health = MaxHealth;
            _timeOfLastDamage = -1000;
        }

        /// <summary>
        /// Time in milliseconds since the last damage this unit received.
        /// </summary>
        public float TimeSinceLastDamage
        {
            get
            {
                return AAGame.ProcessManager.VirtualTime - _timeOfLastDamage;
            }
        }

        /// <summary>
        /// A value which fades from 1 to 0 as time passes since the last damage.
        /// </summary>
        public float LastDamageFade
        {
            get
            {
                float f = 1f - (TimeSinceLastDamage / LastDamageFadeTime);
                f *= DamageMagnitude;

                return MathHelper.Clamp(f, 0, 1);
            }
        }

        /// <summary>
        /// How far are we from being fully healthy?
        /// </summary>
        public float AmountOfDamage
        {
            get
            {
                return MaxHealth - _health;
            }
        }

        public float Health
        {
            get
            {
                return _health;
            }
            set
            {
                float v = MathHelper.Clamp(value, 0, MaxHealth);

                HealthEvent he;
                if(v < _health)
                {
                    he = new HealthEvent(HealthEvent.DAMAGED_EVENT, v - _health, v, _lastDamageOriginator);
                    Owner.EventDispatcher.DispatchEvent(he);
                }

                if(_health > 0 && v == 0)
                {
                    he = new HealthEvent(HealthEvent.DIED_EVENT, v - _health, v, _lastDamageOriginator);
                    Owner.EventDispatcher.DispatchEvent(he);
                }

                if(_health == 0 && v > 0)
                {
                    he = new HealthEvent(HealthEvent.RESURRECTED_EVENT, v - _health, v, _lastDamageOriginator);
                    Owner.EventDispatcher.DispatchEvent(he);
                }

                if(_health > 0 && v > 0)
                {
                    he = new HealthEvent(HealthEvent.HEALED_EVENT, v = _health, v, _lastDamageOriginator);
                    Owner.EventDispatcher.DispatchEvent(he);
                }

                _health = v;

                if(DestroyOnDeath && _health == 0)
                {
                    Owner.Destroy();
                }
            }
        }

        /// <summary>
        /// Apply damage!
        /// </summary>
        /// <param name="amount">Number of HP to debit (positive) or credit (negative).</param>
        /// <param name="type">
        /// String identifier for the type of damage. Used to lookup and apply a damage modifier from DamageModifier.
        /// </param>
        /// <param name="originator">The entity causing the damage, if any.</param>
#if WINDOWS_PHONE || XBOX
        public void Damage(float amount)
        {
            Damage(amount, null);
        }

        public void Damage(float amount, string type)
        {
            Damage(amount, null, null);
        }

        public void Damage(float amount, string type, IEntity originator)
#else
        public void Damage(float amount, string type = null, IEntity originator = null)
#endif
        {
            _lastDamageOriginator = originator;

            // Allow modification of damage based on type.
            if (type != null && DamageModifier.ContainsKey(type))
            {
                amount *= DamageModifier[type];
            }

            // For the flash magnitude, average in preceding fade. 
            DamageMagnitude = MathHelper.Min(1f, (amount / _health) * 4f);
            _timeOfLastDamage = AAGame.ProcessManager.VirtualTime;

            // Apply the damage.
            Health -= amount;

            // If you wanted to do clever things with the last guy to hurt you,
            // you might want to keep this value set. But since it can have GC
            // implications and also lead to stale data we clear it.
            _lastDamageOriginator = null;
        }

        public bool IsDead
        {
            get
            {
                return _health == 0;
            }
        }
    }
}
